﻿//-----------------------------------------------------------------------
// <copyright file="ExpectedRuleViolationAttribute.cs">
//     Copyright (c) Nicole Calinoiu. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.FxCop.Sdk;
using Microsoft.VisualStudio.CodeAnalysis.Extensibility;

namespace Bordecal.FxCop.Sdk.Testing
{
	internal sealed class ExpectedViolationAttributeReader
	{
		internal ExpectedViolationAttributeReader()
		{
		}

		internal IEnumerable<Issue> GetIssues(IEnumerable<string> targetPaths)
		{
			return targetPaths.AsParallel().SelectMany(p => this.GetIssues(ModuleNode.GetModule(p)));
		}

		private IEnumerable<Issue> GetIssues(ModuleNode module)
		{
			return this.GetIssues(new Module(module), module.Attributes)
					.Concat(module.Types.SelectMany(t => this.GetIssues(t)));
		}

		private IEnumerable<Issue> GetIssues(TypeNode type)
		{
			return this.GetIssues(new Type(type), type.Attributes)
					.Concat(type.Members.Except(type.NestedTypes).SelectMany(m => this.GetIssues(m)))
					.Concat(type.NestedTypes.SelectMany(t => this.GetIssues(t)));
		}

		private IEnumerable<Issue> GetIssues(Microsoft.FxCop.Sdk.Member member)
		{
			return this.GetIssues(new Member(member), member.Attributes);
		}

		private IEnumerable<Issue> GetIssues(Target parent, IEnumerable<AttributeNode> allAttributes)
		{
			return allAttributes
					.Where(a => a.Type.FullName == "Bordecal.FxCop.Sdk.Testing.ExpectedRuleViolationAttribute")
					.Select(a => this.CreateIssue(parent, a));
		}

		[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
			Justification = "Performance benefit does not outweigh readability concerns.")]
		private Issue CreateIssue(Target parent, AttributeNode attribute)
		{
			var checkId = attribute.GetPositionalArgumentValue<string>(1);
			var ruleName = attribute.GetPositionalArgumentValue<string>(2);
			var fixCategories = attribute.GetPositionalArgumentValue<string>(3).ToEnum<FixCategories>();
			var messageLevel = attribute.GetPositionalArgumentValue<string>(4).ToEnum<MessageLevel>();
			var certainty = attribute.GetPositionalArgumentValue<int>(5);
			var resolution = attribute.GetPositionalArgumentValue<string>(6);

			var messageId = attribute.GetNamedArgumentValue<string>("MessageId");
			var fileName = attribute.GetNamedArgumentValue<string>("FileName");
			var lineNumber = attribute.GetNamedArgumentValue<int?>("LineNumber");

			return new Issue(
				new Message(parent, checkId, ruleName, messageId, fixCategories),
				certainty,
				fileName,
				lineNumber,
				messageLevel,
				resolution);
		}
	}
}